#include <signal.h>

#include <iostream>
#include <mutex>
#include <string>

#include "common/ad_logger.h"
#include "common/utility.h"
#include "data_adapter.h"
#include "e2e_processor.h"
#include "generated/version.h"
#include "nvidia/cuda_common.h"
#include "nvidia/trt_infer.h"
#include "visualization/perception_visualizer.h"

void exit_safe_func(int sig_num) {
  try {
    std::cout << "[" << autodrive::perception::to_yellow("Warr") << "] "
              << "Caught signal: " << sig_num << ", exit safe." << std::endl;

    auto& dataAdapter{autodrive::perception::DataAdapter::GetInstance()};
    dataAdapter.Stop();

    auto& e2eProcessor{autodrive::perception::E2EProcessor::GetInstance()};
    e2eProcessor.Stop();

    auto& vis{autodrive::perception::PerceptionVisualizer::GetInstance()};
    vis.Close();

  } catch (const std::exception& e) {
    std::cout << "[" << autodrive::perception::to_red("Error") << "]"
              << " BevFusion stop error, " << e.what() << std::endl;
  } catch (...) {
    std::cout << "[" << autodrive::perception::to_red("Error") << "]"
              << " BevFusion stop unexpected error" << std::endl;
  }
  exit(sig_num);
}

void show_info() noexcept {
  std::cout << "************************************" << std::endl;
  std::cout << "*               E2E                *" << std::endl;
  std::cout << "*                                  *" << std::endl;
  std::cout << "* Version: " << PROJECT_VERSION << "                  *"
            << std::endl;
  std::cout << "*                                  *" << std::endl;
  std::cout << "* Compile time: " << COMPILE_TIME << "   *" << std::endl;
  std::cout << "*                                  *" << std::endl;
  std::cout << "************************************" << std::endl;
}

int main(int argc, char const* argv[]) {
  try {
    bool bRet{true};
    std::string errMsg;
    show_info();
    // signal
    signal(SIGINT, exit_safe_func);
    signal(SIGKILL, exit_safe_func);
    signal(SIGBUS, exit_safe_func);
    signal(SIGABRT, exit_safe_func);

    std::cout << "[Info] ADLogger begin to init ......" << std::endl;
    auto& adLogger{autodrive::perception::ADLogger::GetInstance()};
    adLogger.Init("e2e");
    std::cout << "[Info] ADLogger init "
              << autodrive::perception::to_green("OK") << " !!!" << std::endl;

    std::cout << "[Info] DataAdapter begin to init ......" << std::endl;
    auto& dataAdapter{autodrive::perception::DataAdapter::GetInstance()};
    dataAdapter.Init();
    std::cout << "[Info] DataAdapter init "
              << autodrive::perception::to_green("OK") << " !!!" << std::endl;

    std::cout << "[Info] E2EProcessor begin to init ......" << std::endl;
    auto& e2eProcessor{autodrive::perception::E2EProcessor::GetInstance()};
    e2eProcessor.Init();
    std::cout << "[Info] E2EProcessor init "
              << autodrive::perception::to_green("OK") << " !!!" << std::endl;

    std::cout << "[Info] E2EProcessor begin to start ......" << std::endl;
    e2eProcessor.Start();
    std::cout << "[Info] E2EProcessor start "
              << autodrive::perception::to_green("OK") << " !!!" << std::endl;

    std::cout << "[Info] DataAdapter begin to start ......" << std::endl;
    dataAdapter.Start();
    std::cout << "[Info] DataAdapter start "
              << autodrive::perception::to_green("OK") << " !!!" << std::endl;

    ADLOGGER_INFO("E2E start... OK");
    /// wait command to stop
    std::cout << "Welcom! E2E module is running, press "
              << autodrive::perception::to_red("'CTRL+C'") << " to quit!!!"
              << std::endl;

    auto& vis{autodrive::perception::PerceptionVisualizer::GetInstance()};

    while (true) {
      // auto input{std::cin.get()};
      // if (input == 'q') {
      //   std::cout << "quit!!!" << std::endl;
      //   break;
      // }
      if (vis.IsDispose()) {
        break;
      }
      sleep(1);
    }
    vis.Close();
    dataAdapter.Stop();
    e2eProcessor.Stop();
  } catch (const std::exception& e) {
    std::cout << "[" << autodrive::perception::to_red("Error") << "]"
              << " E2E run error, " << e.what() << std::endl;
  } catch (...) {
    std::cout << "[" << autodrive::perception::to_red("Error") << "]"
              << " E2E run unexpected error" << std::endl;
  }
  exit(EXIT_SUCCESS);
}